package Game2048;

import java.awt.Frame;
import java.util.HashMap;
import java.util.Observable;
import java.util.Random;

import javax.swing.JOptionPane;

import model.Model;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import controller.Presenter;
import view.Board;
import view.View;

/**
 * The 2048 Game View. Responsible for all the game view and display.<br>
 * extends {@link Observable}, implements {@link View} & {@link Runnable}
 * @author Barak & Tom
 */
public class Game2048View extends Observable implements View, Runnable {
	Display display;
	Shell shell;
	Board board;
	Label score;
	int userCommand = -1,viewScore = 0,movesToSolve = 0;
	Random rnd = new Random();
	String filePath = "";
	private boolean won = false,lose = false,autoSolver = false;
	

	Image starterCell = new Image(display, "src/Game2048/images/0.png");
	int starterCellH = starterCell.getBounds().height;
	int starterCellW = starterCell.getBounds().width;
	

	private void initComponents() {
		display = new Display();
		shell = new Shell(display);
		shell.setLayout(new GridLayout(4, false));
		shell.setSize(starterCellW * 5, (int) (starterCellH * 4.5));
		shell.setText("2048");
		
		HashMap<Integer, Image> cellsValue = new HashMap<Integer, Image>();
		cellsValue.put(0, new Image(display, "src/Game2048/images/0.png"));
		cellsValue.put(2, new Image(display, "src/Game2048/images/2.png"));
		cellsValue.put(4, new Image(display, "src/Game2048/images/4.png"));
		cellsValue.put(8, new Image(display, "src/Game2048/images/8.png"));
		cellsValue.put(16, new Image(display, "src/Game2048/images/16.png"));
		cellsValue.put(32, new Image(display, "src/Game2048/images/32.png"));
		cellsValue.put(64, new Image(display, "src/Game2048/images/64.png"));
		cellsValue.put(128, new Image(display, "src/Game2048/images/128.png"));
		cellsValue.put(256, new Image(display, "src/Game2048/images/256.png"));
		cellsValue.put(512, new Image(display, "src/Game2048/images/512.png"));
		cellsValue.put(1024, new Image(display, "src/Game2048/images/1024.png"));
		cellsValue.put(2048, new Image(display, "src/Game2048/images/2048.png"));
		
		board = new Board(4, 4, shell, SWT.BORDER,"2048",cellsValue);
		board.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 8, 8)); //TODO: CHANGED FROM 2,2 TO 4,4 !!!!!!

		Menu menuBar = new Menu(shell, SWT.BAR);
		Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
		Menu gameMenu = new Menu(shell, SWT.DROP_DOWN);
		
		MenuItem cascadeGameMenu = new MenuItem(menuBar, SWT.CASCADE);
		cascadeGameMenu.setText("&Game");
		cascadeGameMenu.setMenu(gameMenu);
		
		MenuItem newItem = new MenuItem(gameMenu, SWT.PUSH);
		newItem.setText("&New");
		newItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				clearBoard();
			}
		});

		MenuItem exitItem = new MenuItem(gameMenu, SWT.PUSH);
		exitItem.setText("&Exit");
		exitItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				shell.getDisplay().dispose();
				System.exit(0);
			}
		});
		
        MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE);
        cascadeFileMenu.setText("&File");
        cascadeFileMenu.setMenu(fileMenu);
        
        MenuItem saveItem = new MenuItem(fileMenu, SWT.PUSH);
        saveItem.setText("&Save");
        saveItem.addSelectionListener(new SelectionAdapter() {
        	@Override
        	public void widgetSelected(SelectionEvent e) {
			FileDialog dlg = new FileDialog(shell, SWT.SAVE);
			dlg.setFilterExtensions(new String[] { "*.xml" });
			String fileName = dlg.open();
			if (fileName != "") {
				filePath = fileName;
			}
			userCommand = -2;
			setChanged();
			notifyObservers();
			board.setFocus();
        }
		});
        
        MenuItem loadItem = new MenuItem(fileMenu, SWT.PUSH);
        loadItem.setText("&Load");
        loadItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				FileDialog dlg = new FileDialog(shell, SWT.OPEN);
				dlg.setFilterExtensions(new String[] { "*.xml" });
				String fileName = dlg.open();
				if (fileName != "" && fileName != null) {
					filePath = fileName;
					userCommand = -3;
					setChanged();
					notifyObservers();
				}
				board.setFocus();
			}
		});
        
        shell.setMenuBar(menuBar);
		 
		Button btnUndo = new Button(shell, SWT.PUSH);
		btnUndo.setText("Undo");
		btnUndo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 4, 1));

		Button btnHint = new Button(shell, SWT.PUSH);
		btnHint.setText("Hint");
		btnHint.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 4, 1));
		
		Label lblMovesToPerform = new Label(shell, SWT.BORDER);
		lblMovesToPerform.setText("Moves To Perform:");
		lblMovesToPerform.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
		
		final Text txtMovesToPerform = new Text(shell, SWT.BORDER);
		txtMovesToPerform.setText("1");
		txtMovesToPerform.setLayoutData(new GridData(SWT.FILL, SWT.LEFT, true, false, 1, 1));
		
		Label lblMovesToCalculate = new Label(shell, SWT.BORDER);
		lblMovesToCalculate.setText("Moves To Calculate:");
		lblMovesToCalculate.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
		
		final Combo cmbMovesToCalculate = new Combo(shell, SWT.VERTICAL | SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);
		cmbMovesToCalculate.add("1");
		cmbMovesToCalculate.add("2");
		cmbMovesToCalculate.add("3");
		cmbMovesToCalculate.add("4");
		cmbMovesToCalculate.add("5");
		cmbMovesToCalculate.add("6");
		cmbMovesToCalculate.add("7");
		cmbMovesToCalculate.setText("1");
		cmbMovesToCalculate.setLayoutData(new GridData(SWT.FILL, SWT.LEFT, true, false, 1, 1));

		score = new Label(shell, SWT.NULL);
		score.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
		score.setText("Score:\n" + viewScore);
		
		clearBoard();

		shell.open();

		board.addKeyListener(new KeyListener() {

			@Override
			public void keyReleased(KeyEvent arg0) {
				// TODO Auto-generated method stub

			}

			@Override
			public void keyPressed(KeyEvent e) {
				if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN
						|| e.keyCode == SWT.ARROW_LEFT || e.keyCode == SWT.ARROW_RIGHT) {
					autoSolver = false;
					userCommand = e.keyCode;
					setChanged();
					notifyObservers();
					
				}
			}
		});

		btnUndo.addSelectionListener(new SelectionListener() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				userCommand = -4;
				setChanged();
				notifyObservers();
				
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent arg0) {
				// TODO Auto-generated method stub
				
			}
		
		
		
		});
		
		btnHint.addSelectionListener(new SelectionListener() {
			
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					Integer.parseInt(txtMovesToPerform.getText());
					if (Integer.parseInt(txtMovesToPerform.getText())>-1)  	
					userCommand = Integer.parseInt(cmbMovesToCalculate.getText());
					autoSolver = true;
					movesToSolve = Integer.parseInt(txtMovesToPerform.getText());
					setChanged();
					notifyObservers();
					board.setFocus();
				
				} catch (NumberFormatException e2) {
					board.setFocus();
					
				}
				
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent arg0) {
				// TODO Auto-generated method stub
			}
		});	
	}

	private void clearBoard() {
		userCommand = -1;
		won = false;
		lose = false;
		autoSolver = false;
		setChanged();
		notifyObservers();
//		board.redraw();
	}

	@Override
	public void run() {
		initComponents();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
					if(won){

						Object[] options = {"Restart",
			                    "EXIT"};
						int n = JOptionPane.showOptionDialog(new Frame(),
						    "You won!!! \n what would you like to do? ",
						    "",
						    JOptionPane.YES_NO_CANCEL_OPTION,
						    JOptionPane.QUESTION_MESSAGE,
						    null,
						    options,
						    options[1]);
							if(n == -1 || n == 1){
								shell.getDisplay().dispose();
								System.exit(0);
							}
							else if (n == 0) {
								clearBoard();
								
							}
					}
					
					else if(lose){

						Object[] options = {"Restart",
			                    "EXIT"};
						int n = JOptionPane.showOptionDialog(new Frame(),
						    "You lost... \n what would you like to do? ",
						    "",
						    JOptionPane.YES_NO_CANCEL_OPTION,
						    JOptionPane.QUESTION_MESSAGE,
						    null,
						    options,
						    options[1]);
							if(n == -1 || n == 1){
								shell.getDisplay().dispose();
								System.exit(0);
							}
							else if (n == 0) {
								clearBoard();
								
							}
					}
				if(display != null) display.sleep();
			
				if(autoSolver){  //needs to add a sleep thred to let the player the option to abort the Auto Solver
					if(movesToSolve !=0 ){
						movesToSolve -= 1;
						if (movesToSolve == 1) {
							autoSolver = false;
						}
						setChanged();
						notifyObservers();
					}
					
					//try {
						//Thread.sleep(2000); //TODO: Checking if sleep is needed here
					//} catch (InterruptedException e) {
						// TODO Auto-generated catch block
					//	e.printStackTrace();
					//}
				}
			}
			
		}
		if(display != null) display.dispose();
	}

	/**
	 * Inherited from {@link View}.<br>
	 * Redrawing the game's score and board according to the input parameters.
	 * @param currentScore for game score
	 * @param data (int[][]) for board new state
	 */
	@Override
	public void displayData(final int currentScore, final int[][] data) {
		for (int i = 0; i < data.length; i++) {
			for (int j = 0; j < data[i].length; j++) {
				board.newBoardData[j][i].setValue(data[i][j]);
			}
	}
		score.setText("Score:\n" + currentScore);
		board.setFocus();
		status(data);
	}

	@Override
	public int getUserCommand() {
		if (userCommand != -1) {
			return userCommand;
		}
		return userCommand;
	}

	@Override
	public void addObserver(Presenter p) {
		super.addObserver(p);
	}

	/**
	 * Inherited from {@link Observable}.<br>
	 * Updates the {@link View} by updating the game score and board.
	 * @param currentScore for game score
	 * @param data (int[][]) for board new state
	 */
	@Override
	public void updateMe(int currentScore, int[][] data) {
		displayData(currentScore, data);
	}

	@Override
	public int getCurrentCommand() {
		return userCommand;
	}

	@Override
	public String getFilePath() {
		return filePath;
	}
	
	public void status(int[][] data){
		boolean l = true,w=false;
		for (int i = 0; i < data.length; i++) {
		
			for (int j = 1; j < data[i].length; j++) {
				if (data[i][j] == data[i][j-1] || data[i][j-1] == 0|| data[i][j] == 0) {
					l = false;
					break;
				}
			}
		}
		if(l){
		for (int i = 1; i < data.length; i++) {
			for (int j = 0; j < data[i].length; j++) {
				if (data[i][j] == data[i-1][j]) {
					l = false;
					break;	
				}	
			}
		}
		if (l){lose = true;}
			
		}
		
		for (int i = 0; i < data.length; i++) {
			for (int j = 0; j < data.length; j++) {
				if (data[i][j] == 2048) {
					won =  true;
					
				}
				
			}
			
		}
		
	}
	
	

}
